// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include <gtest/gtest.h>

#include <limits>

#include "vec/common/arithmetic_overflow.h"
#include "vec/core/types.h"
#include "vec/io/io_helper.h"

namespace doris::vectorized {

struct CheckOverFlowTest : public testing::Test {
    void SetUp() override {
        // This function is called before each test.
    }

    void TearDown() override {
        // This function is called after each test.
    }

    Int128 to_i128(std::string str) {
        StringRef str_ref(str.data(), str.size());
        Int128 val;
        EXPECT_TRUE(try_read_int_text(val, str_ref));
        return val;
    };

    wide::Int256 to_i256(std::string str) { return wide::Int256::_impl::from_str(str.c_str()); };
};

TEST_F(CheckOverFlowTest, test_overflow_int128) {
    {
        Int128 a = to_i128("-15687000000000000000000");
        Int128 b = to_i128("11000000000000000");
        Int128 c;
        EXPECT_TRUE(common::mul_overflow(a, b, c));
    }

    {
        Int128 a = to_i128("-15687000000000000000000");
        Int128 b = to_i128("-11000000000000000");
        Int128 c;
        EXPECT_TRUE(common::mul_overflow(a, b, c));
    }

    {
        Int128 a = to_i128("1000");
        Int128 b = to_i128("12000");
        Int128 c;
        EXPECT_FALSE(common::mul_overflow(a, b, c));
    }
}

TEST_F(CheckOverFlowTest, test_overflow_int256) {
    {
        wide::Int256 a =
                to_i256("-11579208923731619542357098500868790785326998466564056403945758400791");
        wide::Int256 b = to_i256("1157920892373161954235709850086879078532699846656405640394575");
        wide::Int256 c;
        EXPECT_TRUE(common::mul_overflow(a, b, c));
    }

    {
        wide::Int256 a = to_i256("-1157920892373161954235709850086879078532699846656405640394");
        wide::Int256 b = to_i256("-1157920892373161954235709850086879078532699846656405640394");
        wide::Int256 c;
        EXPECT_TRUE(common::mul_overflow(a, b, c));
    }

    {
        wide::Int256 a = to_i256("1000");
        wide::Int256 b = to_i256("12000");
        wide::Int256 c;
        EXPECT_FALSE(common::mul_overflow(a, b, c));
    }
    // max int256, 77 decimal digits:
    // >>> 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    // 57896044618658097711785492504343953926634992332820282019728792003956564819967
    // no overflow
    std::vector<std::tuple<std::string, std::string, std::string>> test_input_data = {
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", "0",
             "0"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", "1",
             "-57896044618658097711785492504343953926634992332820282019728792003956564819968"},
            {"57896044618658097711785492504343953926634992332820282019728792003956564819967", "0",
             "0"},
            {"57896044618658097711785492504343953926634992332820282019728792003956564819967", "1",
             "57896044618658097711785492504343953926634992332820282019728792003956564819967"},
            {"57896044618658097711785492504343953926634992332820282019728792003956564819967", "-1",
             "-57896044618658097711785492504343953926634992332820282019728792003956564819967"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819967", "0",
             "0"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819967", "1",
             "-57896044618658097711785492504343953926634992332820282019728792003956564819967"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819967", "-1",
             "57896044618658097711785492504343953926634992332820282019728792003956564819967"},
            {"28948022309329048855892746252171976963317496166410141009864396001978282409983", "2",
             "57896044618658097711785492504343953926634992332820282019728792003956564819966"},
            {"28948022309329048855892746252171976963317496166410141009864396001978282409983", "-2",
             "-57896044618658097711785492504343953926634992332820282019728792003956564819966"},
            {"-28948022309329048855892746252171976963317496166410141009864396001978282409983", "2",
             "-57896044618658097711785492504343953926634992332820282019728792003956564819966"},
            {"-28948022309329048855892746252171976963317496166410141009864396001978282409983", "-2",
             "57896044618658097711785492504343953926634992332820282019728792003956564819966"},
            {"28948022309329048855892746252171976963317496166410141009864396001978282409984", "-2",
             "-57896044618658097711785492504343953926634992332820282019728792003956564819968"},
            {"-28948022309329048855892746252171976963317496166410141009864396001978282409984", "2",
             "-57896044618658097711785492504343953926634992332820282019728792003956564819968"},
            {"9999999999999999999999999999999999999999999999999999999999999999999999999999", "2",
             "19999999999999999999999999999999999999999999999999999999999999999999999999998"},
            {"9999999999999999999999999999999999999999999999999999999999999999999999999999", "-2",
             "-19999999999999999999999999999999999999999999999999999999999999999999999999998"},
            {"-9999999999999999999999999999999999999999999999999999999999999999999999999999", "2",
             "-19999999999999999999999999999999999999999999999999999999999999999999999999998"},
            {"-9999999999999999999999999999999999999999999999999999999999999999999999999999", "-2",
             "19999999999999999999999999999999999999999999999999999999999999999999999999998"},
            {"9999999999999999999999999999999999999999999999999999999999999999999999999999", "5",
             "49999999999999999999999999999999999999999999999999999999999999999999999999995"},
            {"9999999999999999999999999999999999999999999999999999999999999999999999999999", "-5",
             "-49999999999999999999999999999999999999999999999999999999999999999999999999995"},
            {"-9999999999999999999999999999999999999999999999999999999999999999999999999999", "5",
             "-49999999999999999999999999999999999999999999999999999999999999999999999999995"},
            {"-9999999999999999999999999999999999999999999999999999999999999999999999999999", "-5",
             "49999999999999999999999999999999999999999999999999999999999999999999999999995"},
            {"5000000000000000000000000000000000000000000000000000000000000000000000000000", "2",
             "10000000000000000000000000000000000000000000000000000000000000000000000000000"},
            {"5000000000000000000000000000000000000000000000000000000000000000000000000000", "-2",
             "-10000000000000000000000000000000000000000000000000000000000000000000000000000"},
            {"-5000000000000000000000000000000000000000000000000000000000000000000000000000", "2",
             "-10000000000000000000000000000000000000000000000000000000000000000000000000000"},
            {"-5000000000000000000000000000000000000000000000000000000000000000000000000000", "-2",
             "10000000000000000000000000000000000000000000000000000000000000000000000000000"},
            {"240615969168004511545033772477625056927", "240615969168004511545033772477625056927",
             "57896044618658097711785492504343953926579659927927152379400772292519990683329"},
            {"240615969168004511545033772477625056927", "-240615969168004511545033772477625056927",
             "-57896044618658097711785492504343953926579659927927152379400772292519990683329"},
            {"-240615969168004511545033772477625056927", "240615969168004511545033772477625056927",
             "-57896044618658097711785492504343953926579659927927152379400772292519990683329"},
            {"-240615969168004511545033772477625056927", "-240615969168004511545033772477625056927",
             "57896044618658097711785492504343953926579659927927152379400772292519990683329"},
    };
    for (const auto& [input1, input2, expected_str] : test_input_data) {
        // std::cout << "-------Testing multiplication: " << input1 << " * " << input2 << " = "
        //           << expected_str << std::endl;
        wide::Int256 i1 = wide::Int256::_impl::from_str(input1.c_str());
        wide::Int256 i2 = wide::Int256::_impl::from_str(input2.c_str());
        wide::Int256 expect_result = wide::Int256::_impl::from_str(expected_str.c_str());
        // std::cout << "Testing multiplication as int256: " << wide::to_string(i1) << " * "
        //           << wide::to_string(i2) << " = " << wide::to_string(expect_result) << std::endl;
        wide::Int256 mul_res = 0;
        bool overflow = common::mul_overflow(i1, i2, mul_res);
        EXPECT_FALSE(overflow);
        EXPECT_EQ(mul_res, expect_result);
        std::string str_output = wide::to_string(mul_res);
        EXPECT_EQ(str_output, expected_str);

        overflow = common::mul_overflow(i2, i1, mul_res);
        EXPECT_FALSE(overflow);
        EXPECT_EQ(mul_res, expect_result);
        str_output = wide::to_string(mul_res);
        EXPECT_EQ(str_output, expected_str);
    }
    std::vector<std::tuple<std::string, std::string>> test_input_data_overflow = {
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", "2"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819968",
             "-2"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819968",
             "99999999"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819968",
             "-99999999"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819968",
             "57896044618658097711785492504343953926634992332820282019728792003956564819967"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819968",
             "-57896044618658097711785492504343953926634992332820282019728792003956564819967"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819968",
             "-57896044618658097711785492504343953926634992332820282019728792003956564819968"},
            {"57896044618658097711785492504343953926634992332820282019728792003956564819967", "2"},
            {"57896044618658097711785492504343953926634992332820282019728792003956564819967", "-2"},
            {"57896044618658097711785492504343953926634992332820282019728792003956564819967",
             "99999999999999999999999999999999999"},
            {"57896044618658097711785492504343953926634992332820282019728792003956564819967",
             "-99999999999999999999999999999999999"},
            {"57896044618658097711785492504343953926634992332820282019728792003956564819967",
             "57896044618658097711785492504343953926634992332820282019728792003956564819967"},
            {"57896044618658097711785492504343953926634992332820282019728792003956564819967",
             "-57896044618658097711785492504343953926634992332820282019728792003956564819967"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819967", "2"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819967",
             "-2"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819967",
             "99999999999999999999999999999999999"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819967",
             "-99999999999999999999999999999999999"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819967",
             "57896044618658097711785492504343953926634992332820282019728792003956564819967"},
            {"-57896044618658097711785492504343953926634992332820282019728792003956564819967",
             "-57896044618658097711785492504343953926634992332820282019728792003956564819967"},
            {"28948022309329048855892746252171976963317496166410141009864396001978282409984", "2"},
            {"28948022309329048855892746252171976963317496166410141009864396001978282409984", "3"},
            {"-28948022309329048855892746252171976963317496166410141009864396001978282409984", "3"},
            {"-28948022309329048855892746252171976963317496166410141009864396001978282409984",
             "-3"},
            {"-28948022309329048855892746252171976963317496166410141009864396001978282409984",
             "-2"},
            {"99999999999999999999999999999999999", "999999999999999999999999999999999999999999"},
            {"-99999999999999999999999999999999999", "999999999999999999999999999999999999999999"},
            {"99999999999999999999999999999999999", "-999999999999999999999999999999999999999999"},
            {"-99999999999999999999999999999999999", "-999999999999999999999999999999999999999999"},
    };
    for (const auto& [input1, input2] : test_input_data_overflow) {
        wide::Int256 i1 = wide::Int256::_impl::from_str(input1.c_str());
        wide::Int256 i2 = wide::Int256::_impl::from_str(input2.c_str());
        // wide::Int256 mul_res_direct = i1 * i2;
        // std::cout << "multiplication res as int256: " << wide::to_string(i1) << " * "
        //           << wide::to_string(i2) << " = " << wide::to_string(mul_res_direct) << std::endl;
        wide::Int256 mul_res;
        bool overflow = common::mul_overflow(i1, i2, mul_res);
        EXPECT_TRUE(overflow);
        overflow = common::mul_overflow(i2, i1, mul_res);
        EXPECT_TRUE(overflow);
    }
}

} // namespace doris::vectorized
